package bookpad.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import bookpad.common.SignedRequestsHelper;
import bookpad.model.ResultSet;

public class AmazonService extends ServiceBase {
    private static final String AWS_ACCESS_KEY_ID = "";

    private static final String AWS_SECRET_KEY = "";

    private static final String ENDPOINT = "ecs.amazonaws.jp";

    private static final String[] resultTags = {
        "TotalResults",
        "TotalPages",
        "Message" };

    private static final Map<String, Object> itemTags =
        new HashMap<String, Object>() {
            {
                put("ASIN", "@value@");
                put("SalesRank", "@value@");
                put("DetailPageURL", "@value@");
                put("SmallImage", new HashMap<String, Object>() {
                    {
                        put("URL", "@value@");
                        put("Height", "@value@");
                        put("Width", "@value@");
                    }
                });
                put("ItemAttributes", new HashMap<String, Object>() {
                    {
                        put("Author", "@value@");
                        put("Binding", "@value@");
                        put("Creator", "@value@");
                        put("EAN", "@value@");
                        put("ISBN", "@value@");
                        put("Label", "@value@");
                        put("Manufacturer", "@value@");
                        put("Publisher", "@value@");
                        put("Title", "@value@");
                        put("ListPrice", new HashMap<String, Object>() {
                            {
                                put("Amount", "@value@");
                                put("CurrencyCode", "@value@");
                                put("FormattedPrice", "@value@");
                            }
                        });
                    }
                });
                put("EditorialReviews", new HashMap<String, Object>() {
                    {
                        put("EditorialReview", new HashMap<String, Object>() {
                            {
                                put("Source", "@value@");
                                put("Content", "@value@");
                                put("IsLinkSuppressed", "@value@");
                            }
                        });
                    }
                });
            }
        };

    private SignedRequestsHelper helper = null;

    public AmazonService() {
        try {
            helper =
                SignedRequestsHelper.getInstance(
                    ENDPOINT,
                    AWS_ACCESS_KEY_ID,
                    AWS_SECRET_KEY);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }

    public Map<String, String> createCondition() {
        Map<String, String> params = new HashMap<String, String>();
        params.put("Service", "AWSECommerceService");
        params.put("AssociateTag", "27olderistrad-22");
        params.put("Version", "2009-03-31");
        params.put("ItemPage", "1");
        params.put("Sort", "salesrank");
        params.put("ResponseGroup", "Medium");
        return params;
    }

    public List getNoticeList(String searchIndex) {
        String category = "本";
        if ("DVD".equals(searchIndex))
            category = "DVD";
        ResultSet rs = searchList(searchIndex, category, null, "1");
        return rs.getItemList();
    }

    public List getRankingList(String searchIndex, String category,
            String itemPage) {
        ResultSet rs = searchList(searchIndex, category, null, itemPage);
        return rs.getItemList();
    }

    public Map<String, String> getItem(String ASIN) {
        ResultSet rs = searchList(null, null, ASIN, null);
        if (isNull(rs) || isEmpty(rs.getItemList()))
            return null;
        return rs.getItemList().get(0);
    }

    public ResultSet searchList(String searchIndex, String keywords,
            String ASIN, String itemPage) {
        Map<String, String> params = createCondition();
        if (!isNull(keywords)) {
            if (isNull(searchIndex))
                searchIndex = "Books";
            params.put("Operation", "ItemSearch");
            params.put("SearchIndex", searchIndex);
            params.put("Keywords", keywords);
        }
        if (!isNull(ASIN)) {
            params.put("Operation", "ItemLookup");
            params.put("ItemId", ASIN);
        }
        if (!isNull(itemPage))
            params.put("ItemPage", itemPage);
        String requestUrl = helper.sign(params);
        trace("> fetch url >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        trace(requestUrl);
        trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        Map<String, Object> responsMap = fetch(requestUrl);
        trace("# dump start #############################");
        dump(responsMap);
        trace("# dump end ###############################");
        ResultSet rs = new ResultSet();
        rs.setItemList((List<Map>) responsMap.get("itemList"));
        rs.setTotalPages((String) responsMap.get("TotalPages"));
        rs.setTotalResults((String) responsMap.get("TotalResults"));
        // List itemList = (List<Map>) responsMap.get("itemList");
        return rs;
    }

    private static Map<String, Object> fetch(String requestUrl) {
        Map<String, Object> responsMap = new HashMap<String, Object>();
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(requestUrl);
            for (int i = 0; i < resultTags.length; i++) {
                responsMap.put(resultTags[i], getNodeText(doc, resultTags[i]));
            }
            List<Map> itemList = new ArrayList<Map>();
            NodeList itemNodeList = doc.getElementsByTagName("Item");
            for (int i = 0; i < itemNodeList.getLength(); i++) {
                Map itemMap = fetchItem(itemNodeList.item(i));
                itemList.add(itemMap);
            }
            responsMap.put("itemList", itemList);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return responsMap;
    }

    private static Map<String, String> fetchItem(Node itemNode) {
        Map<String, String> respons = new HashMap<String, String>();
        fetchItems(itemNode, itemTags, respons);
        return respons;
    }

    private static void fetchItems(Node node, Map<String, Object> tags,
            Map<String, String> respons) {
        NodeList childs = node.getChildNodes();
        for (int i = 0; i < childs.getLength(); i++) {
            Node c = childs.item(i);
            Object o = tags.get(c.getNodeName());
            if (o instanceof Map) {
                fetchItems(c, (Map) o, respons);
            } else if ("@value@".equals(o)) {
                respons.put(c.getNodeName(), getNodeText(c));
            }
        }
    }

    private static String getNodeText(Node n) {
        Node c = n.getChildNodes().item(0);
        return c.getTextContent();
    }

    private static String getNodeText(Document doc, String key) {
        Node titleNode = doc.getElementsByTagName(key).item(0);
        if (titleNode == null)
            return null;
        return titleNode.getTextContent();
    }

    private void dump(Map<String, Object> respons) {
        Set<String> l = respons.keySet();
        for (String k : l) {
            Object o = respons.get(k);
            if (o instanceof String) {
                trace(k + " : " + respons.get(k));
            } else if (o instanceof List) {
                for (Map map : ((List<Map>) o)) {
                    trace(">>>>> " + map.get("Title"));
                    Set<String> ks = map.keySet();
                    for (String k2 : ks) {
                        trace(k2 + " : " + map.get(k2));
                    }
                }
            }
        }
    }
}
